﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Data;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Media.Imaging;
using CAION2.Classes;
using CAION2.Commands;
using CAION2.Helpers;
using CAION2.Models;

namespace CAION2.ViewModels
{
    internal class ReceptureTreeViewModel : ViewModelBase
    {
        #region Vars

        private Visibility _pageVisible;
        
        private ObservableCollection<TreeNode> _topNodes;
        private TreeNode _rootNode;
        private Dictionary<string, int> _requireMaterialsList;

        private DataRow _rootRecipe;
        private BitmapImage _productIcon;
        private BitmapImage _combo1Icon;
        private string _productQuality;
        private string _combo1Quality;

        private UInt16 _requiredQuantity;
        private bool _checkNeedProduct;
        private bool _checkNeedCraft;

        private string _hideTreeCount;


        #endregion

        #region Properties

        public Visibility PageVisible
        {
            get { return _pageVisible; }
            set
            {
                if (Equals(_pageVisible, value)) return;
                _pageVisible = value;
                OnPropertyChanged("PageVisible");
            }
        }

        public ObservableCollection<TreeNode> TopNodes
        {
            get { return _topNodes; }
            set
            {
                _topNodes = value;
                OnPropertyChanged("TopNodes");
            }
        }

        public Dictionary<string, int> RequireMaterialsList
        {
            get { return _requireMaterialsList; }
            set
            {
                _requireMaterialsList = value;
                OnPropertyChanged("RequireMaterialsList");
            }
        }

        public BitmapImage ProductIcon
        {
            get { return _productIcon; }
            set
            {
                if (Equals(_productIcon, value)) return;
                _productIcon = value;
                OnPropertyChanged("ProductIcon");
            }
        }

        public BitmapImage Combo1Icon
        {
            get { return _combo1Icon; }
            set
            {
                if (Equals(_combo1Icon, value)) return;
                _combo1Icon = value;
                OnPropertyChanged("Combo1Icon");
            }
        }

        public string ProductQuality
        {
            get { return _productQuality; }
            set
            {
                if (Equals(_productQuality, value)) return;
                _productQuality = value;
                OnPropertyChanged("ProductQuality");
            }
        }

        public string Combo1Quality
        {
            get { return _combo1Quality; }
            set
            {
                if (Equals(_combo1Quality, value)) return;
                _combo1Quality = value;
                OnPropertyChanged("Combo1Quality");
            }
        }

        public DataRow RootRecipe
        {
            get { return _rootRecipe; }
            set
            {
                if (Equals(_rootRecipe, value)) return;
                _rootRecipe = value;
                OnPropertyChanged("RootRecipe");
            }
        }

        public UInt16 RequiredQuantity
        {
            get { return _requiredQuantity; }
            set
            {
                if (Equals(_requiredQuantity, value)) return;
                _requiredQuantity = value;
                OnPropertyChanged("RequiredQuantity");

                CalculateFullDemand();
            }
        }

        public bool CheckNeedProduct
        {
            get { return _checkNeedProduct; }
            set
            {
                if (Equals(_checkNeedProduct, value)) return;
                _checkNeedProduct = value;
                OnPropertyChanged("CheckNeedProduct");

                CalculateFullDemand();
            }
        }

        public bool CheckNeedCraft
        {
            get { return _checkNeedCraft; }
            set
            {
                if (Equals(_checkNeedCraft, value)) return;
                _checkNeedCraft = value;
                OnPropertyChanged("CheckNeedCraft");

                CalculateFullDemand();
            }
        }

        public string HideTreeCount
        {
            get { return _hideTreeCount; }
            set
            {
                if (Equals(_hideTreeCount, value)) return;
                _hideTreeCount = value;
                OnPropertyChanged("HideTreeCount");
            }
        }

        #endregion

        #region Commands
        
        public RelayCommand CollapsTreeCommand { get; private set; }
        public RelayCommand ExpandTreeCommand { get; private set; }
        public RelayCommand HideTreeCommand { get; private set; }
        public RelayCommand ShowTreeCommand { get; private set; }
        
        #endregion

        public ReceptureTreeViewModel()
        {
            WireCommands();

            PageVisible = Visibility.Hidden;
            
            Mediator.Instance.Register(o => GetRecipes((Tuple<string, string, string>) o), Messages.RecipeSend);
            Mediator.Instance.Register(o => CalculateFullDemand(), Messages.ReceptureCalculator);

            Mediator.Instance.NotifyColleagues(Messages.RecipeRequire, null);

            RequiredQuantity = 1;
            CheckNeedProduct = true;
        }

        private void WireCommands()
        {
            CollapsTreeCommand = new RelayCommand(FullCollapsTree) { IsEnabled = true };
            ExpandTreeCommand = new RelayCommand(FullExpandTree) { IsEnabled = true };
            HideTreeCommand = new RelayCommand(HideTree) { IsEnabled = true };
            ShowTreeCommand = new RelayCommand(ShowTree) { IsEnabled = true };

            
        }

        private void GetRecipes(Tuple<string, string, string> recipeInfo)
        {
            PageVisible = Visibility.Visible;

            DataView recipes = DataModel.GetDataView2Tables(recipeInfo.Item2, String.Format("qualification_race LIKE '{0}'", recipeInfo.Item3));
            RootRecipe = DataModel.GetRowAtName(recipeInfo.Item2, recipeInfo.Item1,
                String.Format("product, combo1_product, combo2_product, combo3_product, combo4_product, " +
                              "product_translate{0} AS p_translate, combo1_product_translate{0} AS c1_translate, " +
                              "combo2_product_translate{0} AS c2_translate," +
                              "combo3_product_translate{0} AS c3_translate, " +
                              "combo4_product_translate{0} AS c4_translate, " +
                              "product_quantity, required_skillpoint, require_dp", DataModel.EngFix), 
                              "id");

            _rootNode = ClassRecepture.LoadTree(recipes, RootRecipe);
            TopNodes = _rootNode.ChildNodes;

            CalculateFullDemand();
            LoadRecipeInfo();
            UpdateHideTreeCount();

            Mediator.Instance.NotifyColleagues(Messages.ReceptureTitleChange, _rootRecipe["p_translate"].ToString());
        }

        private void CalculateFullDemand()
        {
            if (_rootNode == null) return;
            
            if (CheckNeedProduct)
            {
                double deli = (double)RequiredQuantity/_rootNode.ProductQuantity;
                _rootNode.TotalDemand = Convert.ToUInt16(Math.Ceiling(deli));
            }

            if (CheckNeedCraft)
            {
                _rootNode.TotalDemand = RequiredQuantity;
            }

            CalculateTotalDemandOfMaterials(_rootNode);
            CalculateRequiredMaterialsList();
        }

        private void CalculateTotalDemandOfMaterials(TreeNode parentNode)
        {
            if (parentNode.ChildNodes == null || parentNode.ChildNodes.Count == 0) return;

            foreach (TreeNode node in parentNode.ChildNodes)
            {
                if (node.ProductQuantity > parentNode.ProductQuantity && CheckNeedProduct)
                {
                    double koeff = Math.Ceiling((double)parentNode.TotalDemand / node.ProductQuantity);
                    node.TotalDemand = Convert.ToInt32(koeff * node.ComponentQuantity);
                }
                else
                {
                    node.TotalDemand = parentNode.TotalDemand * node.ComponentQuantity;
                }

                CalculateTotalDemandOfMaterials(node);
            }
        }

        private void CalculateRequiredMaterialsList()
        {
            List<TreeNode> childs =
                _rootNode.ChildNodes.SelectNestedChildren(t => t.ChildNodes).ToList().FindAll(t => t.IsApply);

            RequireMaterialsList = new Dictionary<string, int>();

            foreach (TreeNode child in childs)
            {
                if (child.Item == null) continue;

                string name = child.Item["translate"].ToString();
                if (RequireMaterialsList.ContainsKey(name))
                {
                    RequireMaterialsList[name] = RequireMaterialsList[name] + child.TotalDemand;
                }
                else
                {
                    RequireMaterialsList.Add(name, child.TotalDemand);
                }
            }

            RequireMaterialsList = RequireMaterialsList.OrderByDescending(x => x.Value).ToDictionary(x => x.Key, x => x.Value);
        }
        
        private void LoadRecipeInfo()
        {
            const string fields = "icon_name, quality";

            DataRow product = DataModel.GetRowAtName("ProductsForRecipes", _rootRecipe["product"].ToString(), fields);
            ProductIcon = new BitmapImage(new Uri(DataModel.IconsDirectory + product["icon_name"] + ".png"));
            ProductQuality = product["quality"].ToString();

            if (!string.IsNullOrEmpty(_rootRecipe["combo1_product"].ToString()))
            {
                DataRow combo1 = DataModel.GetRowAtName("ProductsForRecipes", _rootRecipe["combo1_product"].ToString(), fields);
                Combo1Icon = new BitmapImage(new Uri(DataModel.IconsDirectory + combo1["icon_name"] + ".png"));
                Combo1Quality = combo1["quality"].ToString();
            }
            else
            {
                Combo1Icon = null;
                Combo1Quality = "";
            }
            

        }

        private void FullCollapsTree()
        {
            FullTreeManipulation(false, _rootNode);
        }

        private void FullExpandTree()
        {
            FullTreeManipulation(true, _rootNode);
        }

        private void FullTreeManipulation(bool expand, TreeNode parentNode)
        {
            foreach (TreeNode node in parentNode.ChildNodes.Where(node => node.ChildNodes.Any()).Where(node => !node.HasDummyChild))
            {
                node.IsExpanded = expand;
                FullTreeManipulation(expand, node);
            }
        }

        private void HideTree()
        {
            FullTreeVisibleManipulation(true, _rootNode);
            UpdateHideTreeCount();
        }

        private void ShowTree()
        {
            FullTreeVisibleManipulation(false, _rootNode);
            UpdateHideTreeCount();
        }

        private static void FullTreeVisibleManipulation(bool hidden, TreeNode parentNode)
        {
            foreach (TreeNode node in parentNode.ChildNodes)
            {
                if (hidden)
                {
                    node.IsHidden = !node.IsApply;
                }
                else
                {
                    if (node.Id != "dummy") node.IsHidden = false;
                }

                FullTreeVisibleManipulation(hidden, node);
            }
        }

        private void UpdateHideTreeCount()
        {
            int count = TreeviewCountHiddenNodes(_rootNode.ChildNodes);
            HideTreeCount = count > 0 ? count.ToString(CultureInfo.InvariantCulture) : "";
        }

        private static int TreeviewCountHiddenNodes(IEnumerable<TreeNode> childNodes)
        {
            int countchecked = 0;
            if (childNodes == null) return countchecked;

            foreach (TreeNode node in childNodes)
            {
                if (node.ChildNodes.Count == 0 && node.IsHidden)
                {
                    countchecked++;
                }
                else if (node.ChildNodes.Count > 0)
                {
                    countchecked += TreeviewCountHiddenNodes(node.ChildNodes);
                }
            }
            return countchecked;
        }

        
    }

    
    public static class LinqTreeExtension
    {
        public static IEnumerable<T> SelectNestedChildren<T>
            (this IEnumerable<T> source, Func<T, IEnumerable<T>> selector)
        {
            foreach (T item in source)
            {
                yield return item;
                foreach (T subItem in SelectNestedChildren(selector(item), selector))
                {
                    yield return subItem;
                }
            }
        }
    }
    
}

    
